import {
  Controller,
  Get,
  Query,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Song, Album, Artist } from '../entities';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Raw, Any, Like } from 'typeorm';

type ResEntities = Song | Album | Artist;
interface SearchResponse<T> {
  data: T[];
  count: number;
  total: number;
  page: number;
  pageCount: number;
  type: string;
}

@ApiTags('search')
@Controller('resources/search')
// eslint-disable-next-line prettier/prettier
export class SearchController {
  constructor(
    @InjectRepository(Artist)
    private readonly artistsRepo: Repository<Artist>,
    @InjectRepository(Album)
    private readonly albumsRepo: Repository<Album>,
    @InjectRepository(Song)
    private readonly songsRepo: Repository<Song>,
  ) {}

  /**
   * 乐库搜索接口
   * @param s 搜索关键词
   * @param i 搜索类别：`artist` | `album` | `song`
   * @param page 分页页号
   */
  @Get('/')
  async search(
    @Query('s') s: string,
    @Query('i') i: string,
    @Query('page') page: string | number,
  ): Promise<SearchResponse<ResEntities>> {
    if (!i || !s) throw new BadRequestException();
    if (!/^[1-9]\d*$/.test(`${page}`)) page = 1;
    page = typeof page === 'number' ? page : Number.parseInt(page);
    const pageOption = { skip: (page - 1) * 20, take: 20 };

    let found: [ResEntities[], number] = null;
    // TODO: fix sql inject
    // See https://github.com/typeorm/typeorm/issues/4418
    switch (i) {
      case 'song':
        found = await this.songsRepo.findAndCount({
          relations: ['belongsToArtists', 'belongsToAlbum'],
          where: {
            title: Raw(alias => `${alias} ILIKE '%${s}%'`),
          },
          ...pageOption,
        });
        break;
      case 'album':
        found = await this.albumsRepo.findAndCount({
          relations: ['belongsToArtists'],
          where: {
            title: Raw(alias => `${alias} ILIKE '%${s}%'`),
          },
          ...pageOption,
        });
        break;
      case 'artist':
        found = await this.artistsRepo.findAndCount({
          where: [
            {
              name: Raw(alias => `${alias} ILIKE '%${s}%'`),
            },
            {
              alternateNames: Raw(alias => `'${s}' ILIKE ANY (${alias})`),
            },
          ],
          ...pageOption,
        });
        break;
      default:
        throw new NotFoundException();
    }

    return {
      data: found[0],
      count: found[0].length,
      total: found[1],
      page,
      pageCount: Math.ceil(found[1] / pageOption.take),
      type: i,
    };
  }
}
